package com.cl.short_video_java.service.impl;

import com.alipay.api.internal.util.file.IOUtils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cl.short_video_java.common.Const;
import com.cl.short_video_java.dto.UserDto;
import com.cl.short_video_java.mapper.VideoTypeMapper;
import com.cl.short_video_java.mapper.VideoUserMapper;
import com.cl.short_video_java.menum.AliProperties;
import com.cl.short_video_java.menum.VideoSort;
import com.cl.short_video_java.pojo.VideoType;
import com.cl.short_video_java.pojo.VideoUser;
import com.cl.short_video_java.pojo.VideoVideo;
import com.cl.short_video_java.dto.VideoDetail;
import com.cl.short_video_java.exception.WebException;
import com.cl.short_video_java.mapper.*;
import com.cl.short_video_java.pojo.*;
import com.cl.short_video_java.service.SysSettingService;
import com.cl.short_video_java.service.VideoVideoService;
import com.cl.short_video_java.util.FfmpegUtil;
import com.cl.short_video_java.util.JwtUtil;
import com.cl.short_video_java.util.RedisUtil;
import com.cl.short_video_java.vo.sys.VideoInsertReponse;
import com.cl.short_video_java.vo.video.*;
import com.github.pagehelper.PageHelper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.github.pagehelper.PageInfo;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Executor;

/**
 * @author object
 * @description 针对表【video_video】的数据库操作Service实现
 * @createDate 2024-10-31 15:59:00
 */
@Service
@Slf4j
public class VideoVideoServiceImpl extends ServiceImpl<VideoVideoMapper, VideoVideo>
		implements VideoVideoService {

	/**
	 * 临时目录
	 */
	private static final String RELA_FILE = System.getProperty("user.dir") + "/rela";
	/**
	 * 存储目录
	 */
	private static final String STORAGE_FILE = System.getProperty("user.dir") + "/storage";
	private static final double LOOK_RATE = 0.2;
	private static final double LIKE_RATE = 0.3;
	private static final double COLLECT_LIKE = 0.5;
	private static final double TOTAL_RATE = 1;
	@Autowired
	private RedisUtil redisUtil;

	@Autowired
	private VideoUserMapper videoUserMapper;
	@Autowired
	private VideoTypeMapper videoTypeMapper;
	@Autowired
	private VideoVideoMapper videoVideoMapper;
	@Autowired
	private JwtUtil jwtUtil;
	@Autowired
	private SysSettingService settingService;
	@Autowired
	private VideoUserBuyMapper buyMapper;
	@Autowired
	private VideoLikeCollectMapper likeCollectMapper;
	@Autowired
	private GenericObjectPool<FTPClient> genericObjectPool;

	@Autowired
	private VideoHistoryMapper historyMapper;

	@Autowired
	private ModelMapper modelMapper;
	@Autowired
	private Executor asyncExecutor;


	@Override
	public PageInfo<VideoVideo> getPageVideoService(MangementSearch videoMangementSearch) {
		if (videoMangementSearch.getPageNum() == null && videoMangementSearch.getPageSize() == null) {
			videoMangementSearch.setPageNum(1);
			videoMangementSearch.setPageSize(20);
		}
		PageHelper.startPage(videoMangementSearch.getPageNum(), videoMangementSearch.getPageSize());
		QueryWrapper<VideoVideo> wrapper = new QueryWrapper<VideoVideo>()
				.select("id,video_url,create_time,type_floor,first_img_url,look_count,type,gold,thumb_up,collection,status,is_recommend,title,admin_id,user_id")
				.eq("is_delete", 0)
				.eq(videoMangementSearch.getReviewtype() == 0, "status", 0)
				.ne(videoMangementSearch.getReviewtype() == 1, "status", 0)
				.eq(videoMangementSearch.getId() != null, "id", videoMangementSearch.getId())
				.eq(videoMangementSearch.getTypeId() != null, "type", videoMangementSearch.getTypeId())
				.between(videoMangementSearch.getBeginTime() != null && videoMangementSearch.getEndTime() != null, "create_time", videoMangementSearch.getBeginTime(), videoMangementSearch.getEndTime());
		if (!videoMangementSearch.getType().equals("")) {
			List<VideoType> videoTypes = videoTypeMapper.selectList(new QueryWrapper<VideoType>()
					.like("name", videoMangementSearch.getType()));
			if (videoTypes.size() > 0) {
				videoTypes.forEach(element -> {
					wrapper.like("tags", element.getId());
				});
			}
		}
		List<VideoVideo> list = this.list(wrapper);
		list.forEach(item -> {
			VideoUser username = videoUserMapper.selectOne(new QueryWrapper<VideoUser>().eq("id", item.getUserId()));
			if (videoMangementSearch.getReviewtype() == 1) {
				VideoUser adminname = videoUserMapper.selectOne(new QueryWrapper<VideoUser>().eq("id", item.getAdminId()));
				item.setAdminName(adminname.getUsername());
			}
			item.setUserName(username.getUsername());
		});
		List<MangementRespont> pageVideoMapper = videoVideoMapper.getPageVideoMapper(videoMangementSearch);
		PageInfo<VideoVideo> pageInfo = new PageInfo<>(list);
		return pageInfo;
	}

	@Override
	public Object updataTypeService(VideoUpRequest videoUpRequest) {
		Assert.isTrue(videoUpRequest.getId() != null, "视频编号为空");
		VideoVideo videoVideo = new VideoVideo();
		videoVideo.setId(videoUpRequest.getId());
		videoVideo.setType(videoUpRequest.getType());
		videoVideo.setTitle(videoUpRequest.getTitle());
		videoVideo.setGold(videoUpRequest.getGold());
		videoVideo.setCollection(videoUpRequest.getCollection());
		videoVideo.setThumbUp(videoUpRequest.getThumbUp());
		videoVideo.setLookCount(videoUpRequest.getLookCount());
		boolean updateById = this.updateById(videoVideo);
		return updateById;
	}

	@Override
	public Boolean reviewVideoService(String idlist, Integer status, HttpServletRequest httpServletRequest) {
		String token = httpServletRequest.getHeader("token");
		Claims claimByToken = jwtUtil.getClaimByToken(token, Const.GRADE_ADMIN);
		String username = claimByToken.getSubject();
		VideoUser videoUser = videoUserMapper.selectOne(new QueryWrapper<VideoUser>().select("id").eq("username", username));
		if (videoUser != null) {
			if (idlist.length() > 0) {
				String[] split = idlist.split(",");
				Integer list[] = new Integer[split.length];
				for (int i = 0; i < split.length; i++) {
					list[i] = Integer.parseInt(split[i]);
				}
				UpdateWrapper<VideoVideo> wrapper = new UpdateWrapper<>();
				wrapper.in("id", Arrays.asList(list));
				wrapper.set("status", status)
						.set("admin_id", videoUser.getId());
				boolean update = this.update(null, wrapper);
				return update;
			} else {
				throw new RuntimeException("参数为空");
			}
		} else {
			throw new RuntimeException("查出用户为null,请检查token");
		}
	}

	@Override
	public Boolean setIsRecommendService(Integer isrecommend, Integer id) {
		if (isrecommend != null && id != null) {
			boolean update = this.update(new UpdateWrapper<VideoVideo>()
					.eq("id", id)
					.set("is_recommend", isrecommend));
			return update;
		} else {
			throw new RuntimeException("参数为空");
		}
	}

	@Override
	public Boolean deleteService(String idlist) {
		if (idlist.length() > 0) {
			String[] split = idlist.split(",");
			Integer list[] = new Integer[split.length];
			for (int i = 0; i < split.length; i++) {
				list[i] = Integer.parseInt(split[i]);
			}
			UpdateWrapper<VideoVideo> wrapper = new UpdateWrapper<>();
			wrapper.in("id", Arrays.asList(list));
			wrapper.set("is_delete", 1);
			boolean update = this.update(null, wrapper);
			return update;
		} else {
			throw new RuntimeException("参数为空");
		}
	}


	/**
	 * 获取推荐视频 只获取前1000视频
	 */
	@Override
	public void getBank() throws JsonProcessingException {
		log.info("更新推荐视频信息========================================>");
		List<VideoVideo> list = this.list(new QueryWrapper<VideoVideo>()
				.eq("status", 1)
				.eq("is_delete", 0)
				.eq("is_recommend", 1)
				.orderByDesc("sort")
				.last("limit 200")
		);
		int len = list.size();
		if (len < 200) {
			log.info("推荐视频凑数=========={}", len);
			// 不够，其他视频凑一凑
			List<VideoVideo> list1 = this.list(new QueryWrapper<VideoVideo>()
					.eq("status", 1)
					.eq("is_delete", 0)
					.eq("is_recommend", 0)
					.orderByDesc("sort")
					.last("limit " + (200 - len))
			);
			list.addAll(list1);
		}

		for (VideoVideo video : list) {
			VideoType type = videoTypeMapper.selectById(video.getTypeId());
			video.setVideoType(type);
			VideoUser user = videoUserMapper.selectById(video.getUserId());
			video.setUser(user);
		}

		try {
			redisUtil.lLeftPushList("recommend", list);
		} catch (JsonProcessingException e) {
			throw new RuntimeException(e);
		}
	}


	/**
	 * 更新video sort
	 */
	@Override
	public void updateSort() {
		log.info("更新视频sort=============================================>");
		String rate = settingService.getSysSettingByKey("sys_recommend_rate");
		String[] split = rate.split(":");
		Map<String, Double> map = getRate(split);

		List<VideoVideo> list = this.list(new QueryWrapper<VideoVideo>()
				.eq("status", 1)
				.eq("is_delete", 0)
		);
		for (VideoVideo video : list) {
			Long lookCount = video.getLookCount();
			Long collection = video.getCollection();
			Long thumbUp = video.getThumbUp();
			double look = lookCount * map.get("look");
			double collect = collection * map.get("collect");
			double like = thumbUp * map.get("like");
			double sort = look + collect + like;
			video.setSort((int) sort);
		}
		this.saveOrUpdateBatch(list);
	}

	@Override
	public List<?> getRecommon(Integer pageNum) throws JsonProcessingException {
		log.info("更新推荐视频信息========================================>");
		List<VideoVideo> objects = redisUtil.lLeftGetAll("recommend", pageNum);
		if (objects.size() <= 0) {
			this.getBank();
		}
		objects = redisUtil.lLeftGetAll("recommend", pageNum);
		return objects;
	}

	@Override
	public Object upVideoService(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String uuid) throws IOException {
		File files = new File(RELA_FILE);
		String videourl = "";
		String imag = "";
		if (!files.exists()) {
			files.mkdirs();
		}
		File file2 = new File(files, uuid + "_" + chunkIndex);
		chunk.transferTo(file2);
		try {
			if (chunkIndex == (totalChunks - 1)) {
				List<File> inputFile = new ArrayList<>();
				VideoInsertReponse videoInsertReponse = new VideoInsertReponse();
				String id = UUID.randomUUID().toString().replace("-", "");
				File imgFile = new File(settingService.getSysSettingByKey("is_local_img") + "/" + id + ".png");
				File outFile = new File(settingService.getSysSettingByKey("is_local_video") + "/" + id + ".mp4");
				if (totalChunks == 1) {
					inputFile.add(file2);
					imag = FfmpegUtil.videoFrame(file2, imgFile, null);
					videourl = FfmpegUtil.mergeVideo(inputFile, outFile, null);
					videoInsertReponse.setVideoUrl(settingService.getSysSettingByKey("ftp_path") + id + ".mp4");
					videoInsertReponse.setFirstImage(settingService.getSysSettingByKey("ftp_path") + imag);
					return videoInsertReponse;
				} else {
					for (int i = 0; i <= totalChunks - 1; i++) {
						inputFile.add(new File(files, uuid + "_" + i));
					}
					//视频合并上传
					videourl = FfmpegUtil.mergeVideo(inputFile, outFile, settingService.getSysSettingByKey("ffmpeg_path"));
					//提取图片上传
					imag = FfmpegUtil.videoFrame(outFile, imgFile, settingService.getSysSettingByKey("ffmpeg_path"));
					videoInsertReponse.setVideoUrl(settingService.getSysSettingByKey("ftp_path") + id + ".mp4");
					videoInsertReponse.setFirstImage(settingService.getSysSettingByKey("ftp_path") + imag);
					return videoInsertReponse;
				}
			}
			return null;
		} catch (IOException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	@Override
	public String insertVideoService(InserytVideo video, HttpServletRequest httpServletRequest) throws Exception {
		VideoVideo videoVideo = new VideoVideo();
		String token = httpServletRequest.getHeader("token");
		Claims claimByToken = jwtUtil.getClaimByToken(token, Const.GRADE_ADMIN);
		String username = claimByToken.getSubject();
		VideoUser user = videoUserMapper.selectOne(new QueryWrapper<VideoUser>()
				.select("id")
				.eq("username", username));
		videoVideo.setUserId(user.getId());
		if (video.getStatus() != 0) {
			videoVideo.setAdminId(user.getId());
		}
		String type = "";
		List<Integer> typeId = video.getTypeId();
		for (int i = 0; i < typeId.size(); i++) {
			if (i == 0) {
				type = typeId.get(0).toString();
			} else {
				type = type + "," + typeId.get(i).toString();
			}
		}
		videoVideo.setTags(type);
		videoVideo.setTypeId(typeId.get(0));
		videoVideo.setStatus(video.getStatus());
		videoVideo.setType(video.getType());
		videoVideo.setTitle(video.getTitle());
		videoVideo.setDesc(video.getDesc());
		videoVideo.setCreateTime(new Date());
		videoVideo.setGold(video.getGold());
		videoVideo.setFirstImgUrl(video.getFirstImgUrl());
		videoVideo.setVideoUrl(video.getVideoUrl());
		videoVideo.setFirstImgUrl(video.getFirstImgUrl());
		videoVideo.setVideoUrl(video.getVideoUrl());
		try {
			boolean save = this.save(videoVideo);
		} catch (Exception e) {
			//删除临时文件
			e.printStackTrace();
			return e.getMessage();
		} finally {
			return "新增成功";
		}
	}

	@Override
	public ResponseEntity<?> upVideoFtpService(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String
			uuid) throws Exception {
		FTPClient ftpClient = null;
		File files = new File(RELA_FILE);
		File storFile = new File(STORAGE_FILE);
		String videourl = "";
		String imag = "";
		if (!files.exists()) {
			files.mkdir();
		}
		if (!storFile.exists()) {
			storFile.mkdir();
		}
		List<File> inputFile = new ArrayList<>();
		try {
			chunk.transferTo(new File(RELA_FILE + "/" + uuid + "_" + chunkIndex));
			if (chunkIndex == (totalChunks - 1)) {
				for (int i = 0; i < totalChunks; i++) {
					inputFile.add(new File(RELA_FILE + "/" + uuid + "_" + i));
				}
				String id = UUID.randomUUID().toString().replace("-", "");
				File file = new File(RELA_FILE + "/" + id + ".png");
				//提取图片临时存储
				imag = FfmpegUtil.videoFrame(inputFile.get(0), file, settingService.getSysSettingByKey("ffmpeg_path"));
				videourl = id + ".mp4";
				FileInputStream fileInputStream = null;
				ftpClient = genericObjectPool.borrowObject();
				//上传图片
				ftpClient.storeFile(imag, new FileInputStream(file));
				//上传视频
				for (File file1 : inputFile) {
					fileInputStream = new FileInputStream(file1);
					ftpClient.storeFile(videourl, fileInputStream);
				}
				fileInputStream.close();
				genericObjectPool.returnObject(ftpClient);
				VideoInsertReponse videoInsertReponse = new VideoInsertReponse();
				videoInsertReponse.setVideoUrl(videourl);
				videoInsertReponse.setFirstImage(imag);
				//临时文件删除
				for (File file1 : inputFile) {
					boolean delete = file1.delete();
				}
				new File(RELA_FILE + "/" + imag).delete();
				return new ResponseEntity<>(videoInsertReponse, HttpStatus.OK);
			}
			return new ResponseEntity<>(RELA_FILE + "/" + uuid + "_" + chunkIndex, HttpStatus.OK);
		} catch (Exception e) {
			new File(RELA_FILE + "/" + imag).delete();
			for (File file1 : inputFile) {
				file1.delete();
			}
			if (ftpClient != null) {
				ftpClient.deleteFile(videourl);
				ftpClient.deleteFile(imag);
			} else {
				ftpClient = genericObjectPool.borrowObject();
				ftpClient.deleteFile(videourl);
				ftpClient.deleteFile(imag);
				genericObjectPool.returnObject(ftpClient);
			}
			log.info("视频上传错误：" + e.getMessage());
			throw new RuntimeException(e.getMessage());
		}
	}


	/**
	 * 获取视频列表信息
	 *
	 * @param search
	 * @return
	 */
	@Override
	public Page<VideoVideo> getPage(VideoSearch search) {
		log.info("搜索视频-----------------------{},当前索引：{}", search, VideoSort.getValueByKey(search.getSort()));
		QueryWrapper<VideoVideo> queryWrapper = new QueryWrapper<>();
		queryWrapper.like(StringUtils.isNotEmpty(search.getTitle()), "title", search.getTitle())
				.eq("status", 1)
				.eq("is_delete", 0)
				.orderByDesc(VideoSort.getValueByKey(search.getSort()));
		for (Integer typeId : search.getTypeIds()) {
			queryWrapper.like("tags", typeId);
		}
		Page<VideoVideo> page = this.page(new Page<>(search.getPageNum(), 20), queryWrapper);
		for (VideoVideo video : page.getRecords()) {
			String[] split = com.cl.short_video_java.util.StringUtils.isNotEmpty(video.getTags()) ? video.getTags().split(",") : null;
			List<VideoType> id = new ArrayList<>();
			if (split != null) {
				id = videoTypeMapper.selectList(new QueryWrapper<VideoType>().in("id", Arrays.stream(split).toList()));
			}
			video.setVideoTypeList(id);
			VideoUser user = videoUserMapper.selectOne(new QueryWrapper<VideoUser>()
					.select("id,username", "nickname")
					.eq("id", video.getUserId()));
			video.setUser(user);
		}
		return page;

	}

	@Override
	public Map<String, Double> getRate(String[] split) {
		Double look = LOOK_RATE;
		Double like = LIKE_RATE;
		Double collect = COLLECT_LIKE;
		Map<String, Double> map = new HashMap<>();
		map.put("look", look);
		map.put("like", like);
		map.put("collect", collect);

		if (split.length >= 3) {
			Double i = Double.parseDouble(split[0]) / 10;
			Double j = Double.parseDouble(split[1]) / 10;
			Double k = Double.parseDouble(split[2]) / 10;
			if ((i + j + k) != TOTAL_RATE) {
				return map;
			}
			look = i;
			like = j;
			collect = k;
		}
		map.put("look", look);
		map.put("like", like);
		map.put("collect", collect);
		return map;
	}

	/**
	 * 根据用户获取视频列表
	 *
	 * @param uid     用户id
	 * @param current 页码
	 * @return
	 */
	@Override
	public Page<VideoVideo> videoByUser(Integer uid, Integer current) {
		VideoUser user = videoUserMapper.selectById(uid);
		Assert.isTrue(user != null, "当前用户不存在");

		Page<VideoVideo> page = this.page(new Page<>(current, 20), new QueryWrapper<VideoVideo>()
				.eq("user_id", user.getId())
				.eq("is_delete", 0)
				.eq("status", 1)
		);


		return page;
	}

	/**
	 * 获取当前视频类型以及用户是否符合条件
	 *
	 * @param vid
	 * @param user
	 * @return
	 */
	@Override
	public boolean getType(Integer vid, VideoUser user) {
		VideoVideo video = this.getOne(new QueryWrapper<VideoVideo>()
				.eq("id", vid)
				.eq("status", 1)
				.eq("is_delete", 0));
		Assert.isTrue(video.getId() != null, "当前视频信息已丢失");
		Integer level = user.getLevel();
		Integer type = video.getType();

		// 判断当前视频是否购买
		long count = buyMapper.selectCount(new QueryWrapper<VideoUserBuy>()
				.eq("user_id", user.getId())
				.eq("video_id", vid)
				.eq("is_delete", 0));

		boolean b = false;
		if (type == 0) {
			// 免费
			return true;
		} else if (type == 1) {
			// 会员
			return level == 0 ? false : true;
		} else {
			// 金币
			return count > 0;
		}
	}


	/**
	 * 获取视频详细信息
	 *
	 * @param vid
	 * @param uid
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public VideoDetail info(Integer vid, Integer uid) {
		VideoVideo video = this.getOne(new QueryWrapper<VideoVideo>()
				.eq("id", vid)
				.eq("status", 1)
				.eq("is_delete", 0)
		);
		boolean like = getLikeOrCollect(vid, uid, 0);
		boolean collect = getLikeOrCollect(vid, uid, 1);
		log.info("是否点赞收藏了，{},{}", like, collect);
		VideoDetail videoDetail = modelMapper.map(video, VideoDetail.class);
		VideoUser user = videoUserMapper.selectById(video.getUserId());
		videoDetail.setUser(user);
		videoDetail.setIsLike(like);
		videoDetail.setIsCollect(collect);
		long count = historyMapper.selectCount(new QueryWrapper<VideoHistory>().eq("user_id", uid).eq("video_id", vid));
		if (count <= 0) {
			VideoHistory videoHistory = new VideoHistory();
			videoHistory.setVideoId(vid);
			videoHistory.setUserId(uid);
			videoHistory.setCreateTime(LocalDateTime.now());
			historyMapper.insert(videoHistory);
			this.update(new UpdateWrapper<VideoVideo>().eq("id", vid).set("look_count", video.getLookCount() + 1));
		}

		return videoDetail;
	}

	/**
	 * 用户点赞
	 *
	 * @param vid
	 * @param uid
	 * @param model 操作类型：1-点赞，2-取消点赞
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void like(Integer vid, Integer uid, Integer model) {
		VideoVideo video = this.getOne(new QueryWrapper<VideoVideo>().eq("id", vid));
		if (video == null) {
			throw new WebException("当前视频信息不存在");
		}
		Long thumbUp = video.getThumbUp();
		thumbUp = model == 1 ? thumbUp + 1 : thumbUp - 1;
		video.setThumbUp(thumbUp);
		this.updateById(video);
		hobby(vid, uid, model, 1);

	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void collect(Integer vid, Integer uid, Integer model) {
		VideoVideo video = this.getOne(new QueryWrapper<VideoVideo>().eq("id", vid));
		if (video == null) {
			throw new WebException("当前视频信息不存在");
		}

		Long collection = video.getCollection();
		collection = model == 1 ? collection + 1 : collection - 1;
		video.setCollection(collection);
		this.updateById(video);
		hobby(vid, uid, model, 2);
	}

	/**
	 * 用户购买视频
	 *
	 * @param vid
	 * @param uid
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void buy(Integer vid, VideoUser uid) {
		VideoVideo video = this.getOne(new QueryWrapper<VideoVideo>()
				.eq("id", vid)
				.eq("type", 2)
				.eq("status", 1)
				.eq("is_delete", 0)
		);

		if (video == null) {
			throw new WebException("当前视频信息丢失");
		}
		Assert.isTrue(uid.getBalance() >= video.getGold(), "抱歉余额不足");
		Long l = buyMapper.selectCount(new QueryWrapper<VideoUserBuy>().eq("user_id", uid.getId()).eq("video_id", vid));
		if (l > 0) {
//			用户已经购买了
			buyMapper.update(new VideoUserBuy(), new UpdateWrapper<VideoUserBuy>().eq("user_id", uid.getId()).set("is_delete", 1));
		}

		VideoUserBuy videoUserBuy = new VideoUserBuy();
		videoUserBuy.setVideoId(video.getId());
		videoUserBuy.setUserId(uid.getId());
		videoUserBuy.setCreateTime(new Date());
		int insert = buyMapper.insert(videoUserBuy);
		uid.setBalance(uid.getBalance() - video.getGold());
		int i = videoUserMapper.updateById(uid);

	}

	@Override
	public Page<VideoVideo> hit(Integer pageNum) {

		Page<VideoVideo> page = this.page(new Page<>(pageNum, 20), new QueryWrapper<VideoVideo>()
				.eq("status", 1)
				.eq("is_delete", 0)
				.orderByDesc("look_count")
		);
		for (VideoVideo video : page.getRecords()) {
			VideoUser user = videoUserMapper.selectById(video.getUserId());
			if (user != null) {
				UserDto map = modelMapper.map(user, UserDto.class);
				video.setUserDto(map);
			}
		}
		return page;
	}

	@Override
	public Object upVieoOssService(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String uuid) throws IOException {
		File files = new File(RELA_FILE);
		String imag = "";
		String videourl = "";
		if (!files.exists()) {
			files.mkdirs();
		}
		chunk.transferTo(new File(RELA_FILE + "/" + uuid + "_" + chunkIndex));
		if (chunkIndex == (totalChunks - 1) || totalChunks == 1) {
			VideoInsertReponse videoInsertReponse = new VideoInsertReponse();
			Map<String, String> aliOssSetting = settingService.getAliOssSetting();
			OSS ossClient = new OSSClientBuilder().build(aliOssSetting.get(AliProperties.ENDPOINT.getValue())
					, aliOssSetting.get(AliProperties.ACCESS_KEY_ID.getValue())
					, aliOssSetting.get(AliProperties.ACCESS_KEY_SECRET.getValue()));
			String id = UUID.randomUUID().toString().replace("-", "");
			videourl = id + ".mp4";
			//图片提取
			if ((totalChunks - 1) == 0 || totalChunks == 1) {
				//图片和视频上传
				imag = FfmpegUtil.videoFrame(new File(RELA_FILE + "/" + uuid + "_0"), new File(RELA_FILE + "/" + id + ".png"), settingService.getSysSettingByKey("ffmpeg_path"));
				ossClient.putObject(aliOssSetting.get(AliProperties.BUCKET_NAME.getValue()), "img/" + imag, new File(RELA_FILE + "/" + imag));
				ossClient.putObject(aliOssSetting.get(AliProperties.BUCKET_NAME.getValue()), "video/" + videourl, new File(RELA_FILE + "/" + uuid + "_" + chunkIndex));
				videoInsertReponse.setFirstImage(settingService.getSysSettingByKey("ali_img_server_url") + "/img/" + imag);
				videoInsertReponse.setVideoUrl(settingService.getSysSettingByKey("ali_img_server_url") + "/video/" + videourl);
				ossClient.shutdown();
				return videoInsertReponse;
			} else {
				//分片合并
				List<File> input = new ArrayList<>();
				for (int i = 0; i <= (totalChunks - 1); i++) {
					input.add(new File(RELA_FILE + "/" + uuid + "_" + i));
				}
				String video = FfmpegUtil.mergeVideo(input, new File(RELA_FILE + "/" + videourl), settingService.getSysSettingByKey("ffmpeg_path"));
				videourl = video;
				//图片和视频上传
				imag = FfmpegUtil.videoFrame(new File(RELA_FILE + "/" + videourl), new File(RELA_FILE + "/" + id + ".png"), settingService.getSysSettingByKey("ffmpeg_path"));
				ossClient.putObject(aliOssSetting.get(AliProperties.BUCKET_NAME.getValue()), "img/" + imag, new File(RELA_FILE + "/" + imag));
				ossClient.putObject(aliOssSetting.get(AliProperties.BUCKET_NAME.getValue()), "video/" + videourl, new File(RELA_FILE + "/" + videourl));
				videoInsertReponse.setFirstImage(settingService.getSysSettingByKey("ali_img_server_url") + "/img/" + imag);
				videoInsertReponse.setVideoUrl(settingService.getSysSettingByKey("ali_img_server_url") + "/video/" + videourl);
				ossClient.shutdown();
				return videoInsertReponse;
			}
		}
		return null;
	}


	@Override
	public Integer upVideoTemService(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String uuid) {
		// 创建一个临时文件来存储MultipartFile的内容
		File tempFile = null;
		try {
			tempFile = File.createTempFile(uuid + "_" + chunkIndex, ".tmp");
			// 不再需要 deleteOnExit()，我们将在异步任务完成后手动删除它
			chunk.transferTo(tempFile); // 将MultipartFile的内容复制到临时文件
			// 提交异步任务来处理这个临时文件
			File finalTempFile = tempFile;
			asyncExecutor.execute(() -> {
				try {
					// 不再使用锁，因为每个文件分片都有唯一的名称
					File destFile = new File(RELA_FILE, uuid + "_" + chunkIndex);
					Files.copy(finalTempFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
					log.info("保存后的名称：" + uuid + "_" + chunkIndex);
					// 异步任务完成后删除临时文件
					finalTempFile.delete();
				} catch (IOException e) {
					if (finalTempFile != null) {
						finalTempFile.delete();
					}
					log.error("文件保存失败", e);
					// 这里可以添加错误处理和重试逻辑，或者将错误记录到某个持久化存储中
				}
			});

		} catch (IOException e) {
			log.error("文件复制失败", e);
			// 这里可以添加错误处理和返回适当的错误码
			// 抛出异常给调用者，或者返回 null/特殊值来表示失败
			throw new RuntimeException("文件复制失败", e);
		}
		return chunkIndex;
	}

	public void asyncFileInputStream(InputStream inputStream, String uuid, Integer chunkIndex) throws IOException {
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		try {
			fos = new FileOutputStream(RELA_FILE + "/" + uuid + "_" + chunkIndex);
			bos = new BufferedOutputStream(fos);
			byte[] buffer = new byte[1024];
			int length;
			while ((length = inputStream.read(buffer)) > -1) {
				bos.write(buffer, 0, length);
			}
			log.info("我执行完了" + new Date());
		} catch (IOException e) {
			throw new RuntimeException(e);
		} finally {
			if (fos != null || bos != null) {
				bos.close();
				fos.close();
			}
			IOUtils.closeQuietly(inputStream);
		}

	}

	@Override
	public Object VideoMergeService(String uuid, Integer total) throws IOException {
		String isOss = settingService.getSysSettingByKey("is_oss");
		if (isOss.equals("0")) {
			VideoInsertReponse videoInsertReponse = this.upLocalhost(uuid, total);
			return videoInsertReponse;
		} else {
			VideoInsertReponse videoInsertReponse = this.upOss(uuid, total);
			return videoInsertReponse;
		}
	}

	@Override
	public String delVideoService(String img, String video) {
		log.info("我要去删除了");
		int lastIndex = img.lastIndexOf('/');
		int lastvideoIndex = video.lastIndexOf('/');
		String imgname = img.substring(lastIndex + 1);
		String videoname = video.substring(lastvideoIndex + 1);
		if (settingService.getSysSettingByKey("is_oss").equals("0")) {
			String isLocalImg = settingService.getSysSettingByKey("is_local_img");
			String isLocalVideo = settingService.getSysSettingByKey("is_local_video");
			new File(isLocalVideo + "/" + videoname).delete();
			new File(isLocalImg + "/" + imgname).delete();
		} else {
			Map<String, String> aliOssSetting = settingService.getAliOssSetting();
			OSS ossClient = new OSSClientBuilder().build(aliOssSetting.get(AliProperties.ENDPOINT.getValue())
					, aliOssSetting.get(AliProperties.ACCESS_KEY_ID.getValue())
					, aliOssSetting.get(AliProperties.ACCESS_KEY_SECRET.getValue()));
			ossClient.deleteBucketCname(AliProperties.BUCKET_NAME.getValue(), "img/" + imgname);
			ossClient.deleteBucketCname(AliProperties.BUCKET_NAME.getValue(), "video/" + videoname);
		}
		return "ok";
	}

	//视频oss上传方法
	private VideoInsertReponse upOss(String uuid, Integer total) throws IOException {
		List<File> input = new ArrayList<>();
		String imag = "";
		String videourl = "";
		for (int i = 0; i <= (total - 1); i++) {
			input.add(new File(RELA_FILE + "/" + uuid + "_" + i));
		}
		String id = UUID.randomUUID().toString().replace("-", "");
		videourl = id + ".mp4";
		//视频合并
		String video = FfmpegUtil.mergeVideo(input, new File(RELA_FILE + "/" + videourl), settingService.getSysSettingByKey("ffmpeg_path"));
		//图片提取
		imag = FfmpegUtil.videoFrame(new File(RELA_FILE + "/" + videourl), new File(RELA_FILE + "/" + id + ".png"), settingService.getSysSettingByKey("ffmpeg_path"));
		Map<String, String> aliOssSetting = settingService.getAliOssSetting();
		//开启OSS连接
		OSS ossClient = new OSSClientBuilder().build(aliOssSetting.get(AliProperties.ENDPOINT.getValue())
				, aliOssSetting.get(AliProperties.ACCESS_KEY_ID.getValue())
				, aliOssSetting.get(AliProperties.ACCESS_KEY_SECRET.getValue()));
		ossClient.putObject(aliOssSetting.get(AliProperties.BUCKET_NAME.getValue()), "img/" + imag, new File(RELA_FILE + "/" + imag));
		ossClient.putObject(aliOssSetting.get(AliProperties.BUCKET_NAME.getValue()), "video/" + videourl, new File(RELA_FILE + "/" + videourl));
		//关闭Oss连接
		ossClient.shutdown();
		for (File file : input) {
			file.delete();
		}
		VideoInsertReponse videoInsertReponse = new VideoInsertReponse();
		videoInsertReponse.setFirstImage(settingService.getSysSettingByKey("ali_img_server_url") + "/img/" + imag);
		videoInsertReponse.setVideoUrl(settingService.getSysSettingByKey("ali_img_server_url") + "/video/" + videourl);
		return videoInsertReponse;
	}

	//视频本地上传方法
	private VideoInsertReponse upLocalhost(String uuid, Integer total) throws IOException {
		String id = UUID.randomUUID().toString().replace("-", "");
		String isLocalImg = settingService.getSysSettingByKey("is_local_img") + "/img";
		String isLocalVideo = settingService.getSysSettingByKey("is_local_video") + "/video";
		String ftpImgPath = settingService.getSysSettingByKey("ftp_path") + "img/";
		String ftpVideoPath = settingService.getSysSettingByKey("ftp_path") + "video/";
		asyncExecutor.execute(() -> {
			try {
				List<File> inputFiles = new ArrayList<>();
				for (int i = 0; i < total; i++) {
					inputFiles.add(new File(RELA_FILE, uuid + "_" + i));
				}

				File imgDir = new File(isLocalImg);
				if (!imgDir.exists()) {
					imgDir.mkdirs();
				}
				File videoDir = new File(isLocalVideo);
				if (!videoDir.exists()) {
					videoDir.mkdirs();
				}
				File imgFile = new File(isLocalImg, id + ".png");
				File outFile = new File(isLocalVideo, id + ".mp4");
				// 视频合并
				String videourl = FfmpegUtil.mergeVideo(inputFiles, outFile, null);
				if ("0".equals(videourl)) {
					log.error("视频合并失败");
					// 这里可以添加额外的错误处理逻辑，比如通知用户或重试
					return;
				}
				// 图片提取
				String imag = FfmpegUtil.videoFrame(outFile, imgFile, null);
				// 删除临时文件
				for (File file : inputFiles) {
					if (!file.delete()) {
						log.warn("无法删除临时文件: " + file.getAbsolutePath());
					}
				}
				// 这里可以添加逻辑来通知原始调用线程任务已完成，或者将结果存储在某处供后续使用
			} catch (IOException e) {
				log.error("处理视频时发生错误", e);
				// 这里可以添加错误日志记录或通知机制
			}
		});
		// 注意：这里返回的URL可能指向尚未创建的文件
		VideoInsertReponse videoInsertReponse = new VideoInsertReponse();
		videoInsertReponse.setFirstImage(ftpImgPath + id + ".png");
		videoInsertReponse.setVideoUrl(ftpVideoPath + id + ".mp4");
		return videoInsertReponse;
	}

	private boolean getLikeOrCollect(Integer vid, Integer uid, Integer type) {
		long count = likeCollectMapper.selectCount(new QueryWrapper<VideoLikeCollect>()
				.eq("video_id", vid)
				.eq("user_id", uid)
				.eq("type", type)
		);
		if (count > 0) {
			return true;
		}
		return false;
	}

	/**
	 * 修改爱好公共类型
	 *
	 * @param vid
	 * @param uid
	 * @param model
	 * @param type  1-点赞，2-收藏
	 * @return
	 */
	private void hobby(Integer vid, Integer uid, Integer model, Integer type) {
		VideoLikeCollect videoLikeCollect = likeCollectMapper.selectOne(new QueryWrapper<VideoLikeCollect>()
				.eq("user_id", uid)
				.eq("video_id", vid)
				.in("type", Arrays.asList(type == 1 ? 0 : 1, 2))
				.orderByAsc("type")
				.last("limit 1")
		);
		// TODO 优化
		if (videoLikeCollect == null) {
			videoLikeCollect = new VideoLikeCollect();
			videoLikeCollect.setType(model == 1 ? (type == 1 ? 0 : 1) : 2);
			videoLikeCollect.setUserId(uid);
			videoLikeCollect.setVideoId(vid);
			videoLikeCollect.setCreateTime(new Date());
			likeCollectMapper.insert(videoLikeCollect);
		} else {
			videoLikeCollect.setType(model == 1 ? (type == 1 ? 0 : 1) : 2);
			videoLikeCollect.setUpdateTime(new Date());
			likeCollectMapper.updateById(videoLikeCollect);
		}
	}
}




